package study.springcloud.gateway.filter.global;

import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import study.springcloud.gateway.support.utils.ExchangeUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;

@Slf4j
//@Component
public class AccessLogGlobalFilter implements GlobalFilter {

    private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info(">>>>>>>>>>>>>>>>>>>> i am AccessLogGlobalFilter");
        return v2(exchange, chain);
    }

    private Mono<Void> v1(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().pathWithinApplication().value();
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            ServerHttpResponse response = exchange.getResponse();
            HttpStatus statusCode = response.getStatusCode();
            log.info("请求路径:{},远程IP地址:{},响应码:{}", path, remoteAddress, statusCode);
        }));
    }

    private Mono<Void> v2(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
//        String path = request.getPath().pathWithinApplication().value();
        HttpMethod method = request.getMethod();
        StringBuilder sb = new StringBuilder();
        if (HttpMethod.GET.equals(method)) {
            MultiValueMap<String, String> query = request.getQueryParams();
//            sb.append(JsonUtils.toJson(query));
        } else if (HttpMethod.POST.equals(method)) {
            ServerHttpRequest newRequest = request.mutate()
                    .uri(request.getURI())
                    .build();
            Flux<DataBuffer> body = request.getBody();
            body.subscribe(dataBuffer -> {
                InputStream inputStream = dataBuffer.asInputStream();
                try {
                    sb.append(StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8));
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            });

            request = new ServerHttpRequestDecorator(newRequest) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return Flux.just(dataBufferFactory
                            .wrap(sb.toString().getBytes(StandardCharsets.UTF_8)));
                }
            };
        }

        String path = ExchangeUtils.getPath(exchange);
        URI targetURI = ExchangeUtils.getGatewayRequestURI(exchange);
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        ServerWebExchange newExchange = exchange.mutate()
                .request(request)
                .build();
//        return chain.filter(newExchange)
//                .then(Mono.fromRunnable(() -> {
//                    ServerHttpResponse response = exchange.getResponse();
//                    HttpStatus statusCode = response.getStatusCode();
//                    log.info("请求路径:{},远程IP地址:{},请求方法:{},请求参数:{},目标URI:{},响应码:{}",
//                            path, remoteAddress, method, sb.toString(), targetURI, statusCode);
//                }));
        return chain.filter(newExchange);
    }
}
